package com.corn.juc.cas;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author : Jim Wu
 * @version 1.0
 * @function :
 * @since : 2020/12/18 11:08
 */
@Data
@AllArgsConstructor
class User {
    private String name;

    private int age;
}

public class CASDemo1 {

    public static void main(String[] args) {
        // 基本的Atomic CAS应用
//        baseCASdemo();
        // atomicReference
//        atomicRefDemo();
        // atomic Stamp reference
//        ABAProblem();
        // 使用 AtomicStampReference解决ABA问题
        atomicStampRefDemo();

    }

    /**
     * 最基本的 Atomic CAS应用
     */
    private static void baseCASdemo() {
        // 初始化AtomicInteger
        AtomicInteger atomicInteger = new AtomicInteger(1);
        // 判断原值是1 修改成功为100
        atomicInteger.compareAndSet(1, 100);
        System.out.println(atomicInteger.get());
        // 原值已经被修改为100了不是期望值1 此次修改失败
        atomicInteger.compareAndSet(1, 200);
        System.out.println(atomicInteger.get());
    }

    /**
     * 原子引用类使用
     */
    private static void atomicRefDemo() {
        User u1 = new User("jack", 15);
        User u2 = new User("lily", 25);
        User u3 = new User("tom", 45);
        AtomicReference<User> userAtomicReference = new AtomicReference<>(u1);

        System.out.println(userAtomicReference.compareAndSet(u1, u2) + " current user -> " + userAtomicReference.get());
        System.out.println(userAtomicReference.compareAndSet(u1, u3) + " current user -> " + userAtomicReference.get());
    }

    /**
     * ABA 问题演示
     */
    private static void ABAProblem() {
        // 演示ABA问题
        AtomicReference<Integer> num = new AtomicReference<>(5);

        new Thread("t1") {
            @SneakyThrows
            @Override
            public void run() {
                num.compareAndSet(5, 100);
                TimeUnit.SECONDS.sleep(1);
                num.compareAndSet(100, 5);
            }
        }.start();

        new Thread("t2") {
            @SneakyThrows
            @Override
            public void run() {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(num.compareAndSet(5, 200) + " ABA 问题体现 " + num.get());
            }
        }.start();

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("t1 线程执行更快已经将num 从5->100->5 ,t2唤醒之后发现expect=5 " + num.get());
    }

    /**
     * 使用 AtomicStampedReference 通过添加stamp的方法解决ABA问题
     */
    private static void atomicStampRefDemo() {
        AtomicStampedReference<Integer> num = new AtomicStampedReference<>(5, 0);
        new Thread("T1") {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(" t1线程第一次更新: " + num.compareAndSet(5, 100, num.getStamp(), num.getStamp() + 1) + " value = " + num.getReference() + " stamp= " + num.getStamp());
                TimeUnit.SECONDS.sleep(1);
                System.out.println(" t1线程第二次更新: " + num.compareAndSet(100, 5, num.getStamp(), num.getStamp() + 1) + " value = " + num.getReference() + " stamp= " + num.getStamp());
            }
        }.start();
        new Thread("T2") {
            @SneakyThrows
            @Override
            public void run() {
                int stamp = num.getStamp();
                TimeUnit.SECONDS.sleep(2);
                System.out.println("t2 线程尝试更新的stamp " + stamp);
                System.out.println(" t2线程尝试更新: " + num.compareAndSet(5, 200, stamp, stamp + 1));
            }
        }.start();

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("最终num的值为: " + num.getReference());
    }
}
